package com.mdinic.blogovi.service;

import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.annotation.PostConstruct;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.mdinic.blogovi.front.common.login.Credentials;
import com.mdinic.blogovi.model.Follow;
import com.mdinic.blogovi.model.Person;
import com.mdinic.blogovi.service.model.LoginResult;
import com.mdinic.blogovi.service.model.LoginResultType;
import com.mdinic.blogovi.service.model.RegisterResult;
import com.mdinic.blogovi.service.model.UpdatePersonResult;
import com.mdinic.blogovi.service.model.UpdatePersonResultType;

@Service
class PersonServiceImpl implements PersonService {

    private static final Logger LOG = Logger.getLogger(PersonService.class);

    public static String PHOTO_PATH = "p/";
    public static String SMALL_PHOTO_PATH = "sp/";

    @Autowired
    private PasswordService passwordService;

    @Autowired
    private EmailService emailService;

    @Value("${image.upload.path}")
    private String uploadPhotoPath;

    private String uploadPhotoPathPerson;

    private String uploadPhotoPathPersonSmall;

    @PostConstruct
    protected void init() {
        uploadPhotoPathPerson = uploadPhotoPath + PHOTO_PATH;
        uploadPhotoPathPersonSmall = uploadPhotoPath + SMALL_PHOTO_PATH;
        new File(uploadPhotoPathPerson).mkdirs();
        new File(uploadPhotoPathPersonSmall).mkdirs();
    }

    @Override
    public String getUploadPhotoPathPerson() {
        return uploadPhotoPathPerson;
    }

    @Override
    public String getUploadPhotoPathPersonSmall() {
        return uploadPhotoPathPersonSmall;
    }

    @Override
    public RegisterResult register(Person person) {

        if (passwordService.isPasswordStrong(person.getPassword())) {

            List<Person> personByEmail = Person.findPeopleByEmailEquals(person.getEmail()).getResultList();
            if (personByEmail.isEmpty()) {

                List<Person> personByNick = Person.findPeopleByNicknameEquals(person.getNickname()).getResultList();
                if (personByNick.isEmpty()) {
                    person.setComments(0);
                    person.setPosted(0);
                    person.setVotes(0);
                    person.setVotesReceived(0);
                    person.setPromoted(0);

                    person.setJoined(new Date());
                    person.setConfirmed(false);
                    person.setAutoApprove(false);
                    String password = person.getPassword();
                    person.setRegistrationCode(passwordService.generateConfirmCode(person));

                    person.setPassword(passwordService.createEncodedPassword(person.getEmail(), person.getPassword()));

                    person.persist();

                    person.setPassword(password);
                    emailService.sendRegistrationEmail(person);
                    return RegisterResult.OK;
                } else {
                    return RegisterResult.NICK_USED;
                }

            } else {

                return RegisterResult.EMAIL_USED;
            }
        } else {
            return RegisterResult.PASSWORD_WEAK;
        }
    }

    @Override
    public void forgotPassword(String email) {
        List<Person> resultList = Person.findPeopleByEmailEquals(email).getResultList();
        if (resultList.isEmpty()) {
            LOG.info("Non existing user forgot password " + email);
        } else {

            Person person = resultList.get(0);
            String pass = passwordService.generatePassword();
            person.setPassword(passwordService.createEncodedPassword(person.getEmail(), pass));
            person.merge();

            person.setPassword(pass);
            emailService.sendForgotPasswordEmail(person);
        }
    }

    @Override
    public Person confirmRegistration(String code) {
        List<Person> resultList = Person.findPeopleByRegistrationCodeEquals(code).getResultList();
        if (resultList.isEmpty()) {
            return null;
        } else {
            Person person = resultList.get(0);
            person.setConfirmed(true);
            person.setRegistrationCode(null);
            person.merge();
            return person;
        }
    }

    @Override
    public LoginResult login(Credentials credentials) {
        LoginResult result = new LoginResult();
        List<Person> resultList = Person.findConfirmedPeopleByEmailPassword(credentials.getEmail(),
                passwordService.createEncodedPassword(credentials.getEmail(), credentials.getPassword()))
                .getResultList();

        if (resultList.isEmpty()) {
            result.setType(LoginResultType.FAIL);
        } else {
            result.setType(LoginResultType.OK);
            Person person = resultList.get(0);
            person.setToken(passwordService.generateToken());
            person.merge();
            result.setPerson(person);
        }
        return result;
    }

    @Override
    public Person updateImage(long fileName, String image) {
        Person person = Person.findPerson(fileName);
        person.setPicture(image);
        person.merge();
        return person;
    }

    @Override
    public UpdatePersonResult updatePerson(Person person) {
        UpdatePersonResult result = new UpdatePersonResult();
        if (StringUtils.isNotEmpty(person.getPassword()) && !person.getPassword().equals(person.getConfirmPassword())) {
            result.setType(UpdatePersonResultType.PASSWORDS_DO_NOT_MATCH);
            return result;
        } else {

            Person foundPerson = Person.findPerson(person.getId());

            if (!foundPerson.getNickname().equals(person.getNickname())) {
                List<Person> personByEmail = Person.findPeopleByNicknameEquals(person.getNickname()).getResultList();
                if (personByEmail.isEmpty()) {
                    foundPerson.setNickname(person.getNickname());
                } else {
                    result.setType(UpdatePersonResultType.NICKNAME_TAKEN);
                    return result;
                }
            }

            boolean emailChanged = false;
            if (person.getEmail() != null && !foundPerson.getEmail().equals(person.getEmail())) {
                List<Person> personByEmail = Person.findPeopleByEmailEquals(person.getEmail()).getResultList();
                if (personByEmail.isEmpty()) {
                    foundPerson.setTempEmail(person.getEmail());
                    foundPerson.setEmailCode(passwordService.generateConfirmCode(foundPerson));
                    emailChanged = true;
                } else {
                    result.setType(UpdatePersonResultType.EMAIL_TAKEN);
                    return result;
                }
            }
            if (StringUtils.isNotBlank(person.getPassword())) {
                if (passwordService.isPasswordStrong(person.getPassword())) {
                    foundPerson.setPassword(passwordService.createEncodedPassword(person.getEmail(),
                            person.getPassword()));
                } else {
                    result.setType(UpdatePersonResultType.PASSWORD_WEAK);
                    return result;
                }
            }

            foundPerson.setWebsite(person.getWebsite());
            foundPerson.setBlog(person.getBlog());
            foundPerson.setFirstname(person.getFirstname());
            foundPerson.setLastname(person.getLastname());
            foundPerson.merge();
            result.setType(UpdatePersonResultType.OK);
            result.setPerson(foundPerson);

            if (emailChanged) {
                emailService.sendEmailChangeEmail(foundPerson);
            }
        }

        return result;
    }

    @Override
    public void logout(Person person) {
        person.setToken(null);
        person.merge();
    }

    @Override
    public Person confirmNewEmail(String code) {
        List<Person> resultList = Person.findPeopleByEmailCodeEquals(code).getResultList();
        if (resultList.isEmpty()) {
            return null;
        } else {
            Person person = resultList.get(0);
            person.setEmail(person.getTempEmail());
            person.setTempEmail(null);
            person.setEmailCode(null);
            person.merge();
            return person;
        }
    }

    @Override
    public boolean follow(Person person, Person follows) {
        List<Follow> resultList = Follow.findFollowsByPersonAndFollows(person, follows);
        if (resultList.isEmpty()) {
            Follow follow = new Follow();
            follow.setFollows(follows);
            follow.setPerson(person);
            follow.persist();
            return true;
        } else {
            resultList.get(0).remove();
            return false;
        }
    }

    @Override
    public boolean doIFollow(Person person, Person follows) {
        List<Follow> resultList = Follow.findFollowsByPersonAndFollows(person, follows);
        return !resultList.isEmpty();
    }

    @Override
    public List<Person> getFollowing(Person person) {
        List<Follow> resultList = Follow.findFollowsByPerson(person).getResultList();
        List<Person> people = new ArrayList<Person>();
        for (Follow follow : resultList) {
            people.add(follow.getFollows());
        }
        return people;
    }
}
